package com.run2code.design.creational.singleton;

/**
 * 单例模式 - 双重锁校验（DCL）实现
 * 关注公众号【奔跑的码畜】，一起进步不迷路
 *
 * @author 第七人格
 * @date 2023/11/14
 */
public class Singleton_05 {
    /**
     * 实现要点①：将自己的类作为自己的属性，并且加上volatile关键字
     * <p>
     * 注意：
     * 这里加上volatile关键字的原因是，防止指令重排序，保证单例的唯一性。
     * 因为new这个操作，并不是原子性的，他有3步：
     * 1、分配内存，在jvm堆中分配一段区域
     * 2、初始化对象，在jvm堆中的内存中实例化对象
     * 3、赋值，将对象指向堆中的内存地址
     * 这里如果指令重排序，那么可能2和3的顺序会颠倒，在多线程下那么就可能出现多个对象，违背了单例模式
     */
    private static volatile Singleton_05 INSTANCE;

    /**
     * 实现要点②：私有化构造函数，防止被外部实例化
     */
    private Singleton_05() {
    }

    /**
     * 实现要点⑥：对外提供获取实例的静态方法
     *
     * @return {@link Singleton_05}
     */
    public static Singleton_05 getInstance() {
        // 实现要点③：第一层检查，检查是否有引用指向对象，高并发情况下会有多个线程同时进入
        if (null == INSTANCE) {
            // 实现要点④：锁对象
            synchronized (Singleton_05.class) {
                // 实现要点⑤：第二层检查，防止除了进入的第一个线程的其他线程重复创建对象
                if (null == INSTANCE) {
                    INSTANCE = new Singleton_05();
                }
            }
        }
        return INSTANCE;
    }

}
